﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using ThoughtWorks.QRCode.Codec;

namespace X.QRCode
{
    /// <summary>
    /// 二维码帮助类
    /// <para>基于第三方二维码生成类（ThoughtWorks.QRCode）二次封装</para>
    /// </summary>
    public class QRCodeHelper
    {
        /*
        version:
            QrcodeVersion的范围值是0-40,0的含义是表示压缩的信息量将会根据实际传入值确定，只有最高上限的控制，而且图片的大小将会根据信息量自动缩放。1-40的范围值，则有固定的信息量上限，而且图片的大小会固定在一个大小上，不会根据信息量的多少而变化。
        scale:
            生成二维码每个小方格的尺寸（单位像素）
        */

        /// <summary>
        /// 编码模式
        /// </summary>
        public enum QREncoding
        {
            AlphaNumeric,
            /// <summary>
            /// 数值模式
            /// </summary>
            Numeric,
            /// <summary>
            /// 字节编码
            /// </summary>
            Byte,
        }

        /// <summary>
        /// 纠错等级
        /// </summary>
        public enum QRLevel
        {
            /// <summary>
            /// 约可纠错7%的数据码字
            /// </summary>
            L,
            /// <summary>
            /// 约可纠错15%的数据码字
            /// </summary>
            M,
            /// <summary>
            /// 约可纠错25%的数据码字
            /// </summary>
            Q,
            /// <summary>
            /// 约可纠错30%的数据码字
            /// </summary>
            H
        }

        private QREncoding _Encoding = QREncoding.Byte;

        /// <summary>
        /// 编码模式
        /// </summary>
        public QREncoding Encoding
        {
            get { return _Encoding; }
            set { _Encoding = value; }
        }

        private QRLevel _Level = QRLevel.M;

        /// <summary>
        /// 纠错等级
        /// </summary>
        public QRLevel Level
        {
            get { return _Level; }
            set { _Level = value; }
        }

        private int _Version = 6;
        /// <summary>
        /// 版本
        /// <para>0-40 默认6</para>
        /// </summary>
        public int Version
        {
            get { return _Version; }
            set { _Version = value; }
        }

        private int _Scale = 4;

        /// <summary>
        /// 生成级别
        /// <para>每个数据格的像素大小，默认4</para>
        /// </summary>
        public int Scale
        {
            get { return _Scale; }
            set { _Scale = value; }
        }


        /// <summary>
        /// 获取二维码的Image对象
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public Image CreateImage(string data)
        {
            return CreateQRCode(data);
        }

        /// <summary>
        /// 生成二维码并写入保存到指定路径
        /// </summary>
        /// <param name="filepath"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool CreateFile(string filepath, string data)
        {
            try
            {
                var img = CreateQRCode(data);
                if (File.Exists(filepath))
                    throw new Exception("目标文件已存在！");
                string dir = filepath.Substring(0, filepath.LastIndexOf('\\') + 1);
                if (!System.IO.Directory.Exists(dir))
                    System.IO.Directory.CreateDirectory(dir);
                img.Save(filepath, System.Drawing.Imaging.ImageFormat.Jpeg);
                return true;
            }catch(Exception ex)
            {
#if DEBUG
                throw ex;
#else
                return false;
#endif
            }
        }

        /// <summary>
        /// 获取二维码Base64编码后的字符串
        /// </summary>
        /// <returns></returns>
        public string CreateBase64String(string data)
        {
            var img = CreateQRCode(data);
            return GetBase64String(img);
        }

        /// <summary>
        /// 创建二维码图像对象
        /// </summary>
        /// <param name="data">要生成二维码的文本数据</param>
        /// <returns></returns>
        private Image CreateQRCode(string data)
        {
            QRCodeEncoder qrCodeEncoder = new QRCodeEncoder();

            switch (Encoding)
            {
                case QREncoding.Byte:
                    qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE;
                    break;
                case QREncoding.AlphaNumeric:
                    qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.ALPHA_NUMERIC;
                    break;
                case QREncoding.Numeric:
                    qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.NUMERIC;
                    break;
                default:
                    qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE;
                    break;
            }
            qrCodeEncoder.QRCodeScale = Scale;
            qrCodeEncoder.QRCodeVersion = Version;
            switch (Level)
            {
                case QRLevel.L:
                    qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.L;
                    break;
                case QRLevel.M:
                    qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.M;
                    break;
                case QRLevel.Q:
                    qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.Q;
                    break;
                case QRLevel.H:
                    qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.H;
                    break;
                default:
                    qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.H;
                    break;
            }
            return qrCodeEncoder.Encode(data);
        }


        private static string GetBase64String(Image image)
        {
            try
            {
                string result = string.Empty;
                using (var memStream = new MemoryStream())
                {
                    image.Save(memStream, System.Drawing.Imaging.ImageFormat.Jpeg);
                    var bytes = new byte[memStream.Length];
                    memStream.Position = 0;
                    memStream.Read(bytes, 0, bytes.Length);
                    result = Convert.ToBase64String(bytes);
                }
                return result;
            }
            catch
            {
                return string.Empty;
            }
        }

    }
}
